home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 03 - 1987 / 03.05 May 87 / undo source / TML source / UndoIt.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-07-24  |  13.9 KB  |  573 lines  |  [TEXT/EDIT]

  1. PROGRAM UndoIt;
  2.  
  3. {Program to test Edit menu including Undo/Redo of previous operation}
  4.  
  5. {Copyright 1986 (M)agreeable software, inc.}
  6. {               5925 Magnolia Lane         }
  7. {               Plymouth MN 55442          }
  8.  
  9. {$L UndoIt/Rsrc}
  10.  
  11.  
  12. {$I Memtypes.Ipas }
  13. {$I QuickDraw.Ipas}
  14. {$I OSIntf.Ipas   }
  15. {$I ToolIntf.Ipas }
  16. {$I PackIntf.Ipas }
  17.  
  18. CONST MenuBarID   = 200;
  19.       FileMenu    = 200;
  20.       OpenItem    =   1;
  21.       CloseItem   =   2;
  22.       QuitItem    =   4;
  23.       EditMenu    = 201;
  24.       UndoItem    =   1;
  25.       CutItem     =   3;
  26.       CopyItem    =   4;
  27.       PasteItem   =   5;
  28.       ClearItem   =   6;
  29.       WindowID    = 200;
  30.       CantUndoStr = 'Can''t Undo'; {Note double apostrophe}
  31.       UndoStr      = 'Undo';
  32.       RedoStr      = 'Redo';
  33.       TypingStr      = 'Typing';
  34.  
  35. TYPE EditType = (CantUndo,UndoTyping,UndoCut,UndoCopy,
  36.               UndoPaste,UndoClear,
  37.               RedoTyping,RedoCut,RedoCopy,
  38.               RedoPaste,RedoClear);
  39.  
  40. {Global variables}
  41.  
  42. VAR theWindow    : WindowPtr; {Main window}
  43.     DisplayTE    : TEHandle;  {TextEdit record for display}
  44.     ScrapTE      : TEHandle;  {TextEdit record for scrap}
  45.     fNum         : integer;   {Ref number for current file}
  46.     QuitFlag     : Boolean;   {Main event loop exits when TRUE}
  47.     UndoStart      : integer;   {Start of previous/current selection}
  48.     UndoEnd     : integer;   {End of previous/current selection}
  49.     CurrentStart : integer;   {Backspace point before previous selection}
  50.     EditStatus   : EditType;
  51.     FileHandle   : MenuHandle;
  52.     EditHandle   : MenuHandle;
  53.  
  54. PROCEDURE Init_MyGlobals;
  55.  
  56.  BEGIN
  57.   QuitFlag:=FALSE;
  58.   theWindow:=NIL; {=NIL if no window opened}
  59.  END; {Init_MyGlobals}
  60.  
  61. PROCEDURE Open_File;
  62.  
  63.  CONST hCorner   =    90;
  64.        vCorner   =    90;
  65.        MaxTEText = 32767;
  66.        
  67.  VAR OpenReply  : SFReply;
  68.      GetWhere   : Point;
  69.      fTypes     : SFTypeList;
  70.      OpenErr    : OSErr;
  71.      TextRect   : Rect;
  72.      TextLength : LongInt;
  73.      TextDest   : Ptr;
  74.  
  75.  BEGIN
  76.   GetWhere.h:=hCorner;
  77.   GetWhere.v:=vCorner;
  78.   fTypes[0]:='TEXT';
  79.   OpenErr:=-1;           {Set to other than noErr}
  80.   SFGetFile(GetWhere,'',NIL,1,fTypes,NIL,OpenReply);
  81.   WITH OpenReply DO
  82.    IF Good THEN
  83.     OpenErr:=FSOpen(fName,vRefNum,fNum);
  84.   IF OpenErr=noErr THEN
  85.    BEGIN
  86.     theWindow:=GetNewWindow(windowID,NIL,WindowPtr(-1));
  87.     SetPort(theWindow);
  88.     TextRect:=theWindow^.portRect;
  89.     DisplayTE:=TENew(TextRect,TextRect);
  90.     WITH TextRect DO                 {Make ScrapTE "invisible"}
  91.      BEGIN
  92.       top:=-bottom;
  93.       left:=-right;
  94.       bottom:=0;
  95.       right:=0;
  96.      END;
  97.     ScrapTE:=TENew(TextRect,TextRect);
  98.     OpenErr:=GetEof(fNum,TextLength);
  99.     IF TextLength>MaxTEText THEN       {Ensure "not too much" read}
  100.      TextLength:=MaxTEText;
  101.     TextDest:=NewPtr(TextLength);
  102.     OpenErr:=SetFPos(fNum,fsFromStart,0);     {Read text from file}
  103.     OpenErr:=FSRead(fNum,TextLength,TextDest);
  104.     TEInsert(TextDest,TextLength,DisplayTE);
  105.     DisposPtr(TextDest);
  106.     EnableItem(FileHandle,CloseItem);
  107.     DisableItem(FileHandle,OpenItem);
  108.    END; {IF OpenErr=noErr}
  109.  END; {Open_File}
  110.  
  111. PROCEDURE Close_File;
  112.  
  113.  VAR CloseErr   : OSErr;
  114.  
  115.  BEGIN
  116.   HideWindow(theWindow);
  117.   TEDispose(DisplayTE);
  118.   TEDispose(ScrapTE);
  119.   DisposeWindow(theWindow);
  120.   theWindow:=NIL;
  121.   CloseErr:=FSClose(fNum);
  122.   EnableItem(FileHandle,OpenItem);
  123.   DisableItem(FileHandle,CloseItem);
  124.  END; {Close_File}
  125.  
  126. PROCEDURE File_Manager(MenuItem:integer;VAR QuitFlag:Boolean);
  127.  
  128.  BEGIN
  129.   CASE MenuItem OF
  130.    OpenItem:Open_File;
  131.    CloseItem:Close_File;
  132.    QuitItem:
  133.     BEGIN
  134.      IF theWindow<>NIL THEN
  135.       Close_File;
  136.      QuitFlag:=TRUE;
  137.     END;
  138.   OTHERWISE
  139.   END; {CASE MenuItem}
  140.  END; {File_Manager}
  141.  
  142. PROCEDURE Reset_EditMenu(UndoState:EditType);
  143.  
  144.  VAR theStr     : Str255;
  145.      theItem    : integer;
  146.  
  147.  BEGIN
  148.   IF TEGetScrapLen>0 THEN          {Set Paste according to scrap}
  149.    EnableItem(EditHandle,PasteItem)
  150.   ELSE
  151.    DisableItem(EditHandle,PasteItem);
  152.   WITH DisplayTE^^ DO
  153.    IF SelStart<SelEnd THEN        {Set Cut,Copy,Clear according}
  154.     BEGIN                           {to selection size           }
  155.      EnableItem(EditHandle,CutItem);
  156.      EnableItem(EditHandle,CopyItem);
  157.      EnableItem(EditHandle,ClearItem);
  158.     END
  159.    ELSE
  160.     BEGIN
  161.      DisableItem(EditHandle,CutItem);
  162.      DisableItem(EditHandle,CopyItem);
  163.      DisableItem(EditHandle,ClearItem);
  164.     END;
  165.   EditStatus:=UndoState;
  166.   IF EditStatus=CantUndo THEN
  167.    theStr:=CantUndoStr
  168.   ELSE IF EditStatus IN [UndoTyping,RedoTyping] THEN
  169.    theStr:=TypingStr
  170.   ELSE
  171.    BEGIN
  172.     CASE EditStatus OF         {Get item number to Undo/Redo}
  173.      UndoCut,RedoCut:
  174.       theItem:=CutItem;
  175.      UndoCopy,RedoCopy:
  176.       theItem:=CopyItem;
  177.      UndoPaste,RedoPaste:
  178.       theItem:=PasteItem;
  179.      UndoClear,RedoClear:
  180.       theItem:=ClearItem;
  181.      OTHERWISE
  182.     END; {CASE EditStatus}
  183.     GetItem(EditHandle,theItem,theStr);
  184.    END; {IF EditStatus}
  185.   IF EditStatus IN [UndoTyping..UndoClear] THEN
  186.    theStr:=Concat(UndoStr,' ',theStr)
  187.   ELSE IF EditStatus IN [RedoTyping..RedoClear] THEN
  188.    theStr:=Concat(RedoStr,' ',theStr);
  189.   SetItem(EditHandle,UndoItem,theStr);     {Reset Undo item}
  190.   IF EditStatus=CantUndo THEN              {Disable Can't Undo, Enable Undo/Redo}
  191.    DisableItem(EditHandle,UndoItem)
  192.   ELSE
  193.    EnableItem(EditHandle,UndoItem);
  194.  END; {Reset_EditMenu}
  195.  
  196. PROCEDURE Save_Clipboard;          
  197.  
  198.  BEGIN
  199.   TESetSelect(0,ScrapTE^^.TELength,ScrapTE);
  200.   TEPaste(ScrapTE);
  201.  END; {Save_Clipboard}
  202.  
  203. PROCEDURE Restore_Clipboard;
  204.  
  205.  BEGIN
  206.   TESetSelect(0,ScrapTE^^.TELength,ScrapTE);
  207.   TECut(ScrapTE);                            {Also clears ScrapTE}
  208.  END; {Restore_Clipboard}
  209.  
  210. PROCEDURE Delete_ScrapTE;
  211.  
  212.  BEGIN
  213.   TESetSelect(0,ScrapTE^^.TELength,ScrapTE);
  214.   TEDelete(ScrapTE);
  215.  END; {Delete_ScrapTE}
  216.  
  217. PROCEDURE Save_EndPoints;
  218.  
  219.  BEGIN
  220.   WITH DisplayTE^^ DO          {Save selection points}
  221.    BEGIN
  222.     UndoStart:=SelStart;
  223.     UndoEnd:=SelEnd;
  224.     CurrentStart:=UndoStart;
  225.    END;
  226.  END; {Save_EndPoints}
  227.  
  228. PROCEDURE Save_Selection(theStart,theEnd:integer);   {Selection to ScrapTE}
  229.  
  230.  BEGIN
  231.   IF theStart<theEnd THEN
  232.    BEGIN
  233.     HLock(Handle(DisplayTE));
  234.     WITH DisplayTE^^ DO
  235.      BEGIN
  236.       HLock(hText);
  237.       TEInsert(Ptr(Ord4(hText^)+theStart),
  238.                theEnd-theStart,ScrapTE);
  239.       HUnlock(hText);
  240.      END;
  241.     HUnlock(Handle(DisplayTE));
  242.    END;
  243.  END; {Save_Selection}
  244.  
  245. PROCEDURE Restore_Selection(theLength:integer);  {ScrapTE to selection}
  246.  
  247.  BEGIN
  248.   IF theLength>0 THEN
  249.    BEGIN
  250.     HLock(Handle(ScrapTE));
  251.     WITH ScrapTE^^ DO
  252.      BEGIN
  253.       HLock(hText);                    {ScrapTE to insertion point}
  254.       TEInsert(Ptr(Ord4(hText^)),theLength,DisplayTE);
  255.       HUnlock(hText);
  256.      END;
  257.     HUnlock(Handle(ScrapTE));
  258.    END;
  259.  END; {Restore_Selection}
  260.  
  261. PROCEDURE Cut;
  262.  
  263.  BEGIN 
  264.   Save_EndPoints;
  265.   Save_Clipboard;
  266.   TECut(DisplayTE);      {Cut selection to clipboard}
  267.   Reset_EditMenu(UndoCut);
  268.  END; {Cut}
  269.  
  270. PROCEDURE Copy;
  271.  
  272.  BEGIN
  273.   Save_EndPoints;
  274.   Save_Clipboard;        {Save old clipboard}
  275.   TECopy(DisplayTE);     {Copy selection to clipboard}
  276.   Reset_EditMenu(UndoCopy);
  277.  END; {Copy}
  278.  
  279. PROCEDURE Paste;
  280.  
  281.  BEGIN
  282.   Save_EndPoints;
  283.   Delete_ScrapTE;
  284.   Save_Selection(UndoStart,UndoEnd);
  285.   TEPaste(DisplayTE);      {Paste selection to clipboard}
  286.   Reset_EditMenu(UndoPaste);
  287.  END; {Paste}
  288.  
  289. PROCEDURE Clear;
  290.  
  291.  BEGIN
  292.   Save_EndPoints;
  293.   Delete_ScrapTE;
  294.   Save_Selection(UndoStart,UndoEnd);
  295.   TEDelete(DisplayTE);       {Delete current selection}
  296.   Reset_EditMenu(UndoClear);
  297.  END; {Clear}
  298.  
  299. PROCEDURE Undo_Cut;
  300.  
  301.  BEGIN
  302.   TEPaste(DisplayTE);                        {Restore text}
  303.   TESetSelect(UndoStart,UndoEnd,DisplayTE);  {Reset selection}
  304.   Restore_Clipboard;
  305.   Reset_EditMenu(RedoCut);
  306.  END; {Undo_Cut}
  307.  
  308. PROCEDURE Undo_Copy;
  309.  
  310.  BEGIN
  311.   Restore_Clipboard;
  312.   Reset_EditMenu(RedoCopy);
  313.  END; {Undo_Copy}
  314.  
  315. PROCEDURE Undo_Paste;
  316.  
  317.  BEGIN
  318.   TESetSelect(UndoStart,DisplayTE^^.SelEnd,DisplayTE); {Delete pasted text}
  319.   {UndoStart is also beginning of pasted text}
  320.   TEDelete(DisplayTE);
  321.   Restore_Selection(ScrapTE^^.TELength);
  322.   TESetSelect(UndoStart,UndoEnd,DisplayTE);  {Reset selection}
  323.   Delete_ScrapTE;
  324.   Reset_EditMenu(RedoPaste);
  325.  END; {Undo_Paste}
  326.  
  327. PROCEDURE Undo_Clear;
  328.  
  329.  BEGIN
  330.   Restore_Selection(ScrapTE^^.TELength);
  331.   TESetSelect(UndoStart,UndoEnd,DisplayTE);  {Reset selection}
  332.   Delete_ScrapTE;
  333.   Reset_EditMenu(RedoClear);
  334.  END; {Undo_Clear}
  335.  
  336. PROCEDURE Undo_Typing;
  337.  
  338.  VAR scrapLength : integer;
  339.      TypingEnd   : integer;
  340.      theText     : Handle;
  341.  
  342.  BEGIN
  343.   scrapLength:=ScrapTE^^.TELength; {Put new typing at end of scrap}
  344.   TESetSelect(scrapLength,scrapLength,ScrapTE);
  345.   TypingEnd:=DisplayTE^^.selEnd;
  346.   IF CurrentStart<TypingEnd THEN
  347.    BEGIN
  348.     Save_Selection(CurrentStart,TypingEnd);
  349.     TESetSelect(CurrentStart,TypingEnd,DisplayTE);
  350.     TEDelete(DisplayTE);       {Delete new typing}
  351.    END; {IF CurrentStart<TypingEnd}
  352.   Restore_Selection(scrapLength);   {Put original selection back}
  353.   TESetSelect(UndoStart,UndoEnd,DisplayTE);
  354.   TESetSelect(0,scrapLength,ScrapTE); {Delete previous from scrap}
  355.   TEDelete(ScrapTE);
  356.   Reset_EditMenu(RedoTyping);
  357.  END; {Undo_Typing}
  358.  
  359. PROCEDURE Redo_Typing;
  360.  
  361.  VAR scrapLength : integer;
  362.      TypingEnd   : integer;
  363.      theText     : Handle;
  364.  
  365.  BEGIN
  366.   scrapLength:=ScrapTE^^.TELength; {Put old selection at end of }    
  367.   TESetSelect(scrapLength,scrapLength,ScrapTE);  {ScrapTE}
  368.   TypingEnd:=DisplayTE^^.selEnd;
  369.   IF CurrentStart<TypingEnd THEN
  370.    BEGIN
  371.     Save_Selection(CurrentStart,TypingEnd);
  372.     TESetSelect(CurrentStart,TypingEnd,DisplayTE);
  373.     TEDelete(DisplayTE);            {Delete old selection}
  374.    END; {IF CurrentStart<TypingEnd}
  375.   Restore_Selection(scrapLength);   {Move new typing to DisplayTE}
  376.   TESetSelect(0,scrapLength,ScrapTE);
  377.   TEDelete(ScrapTE);                {Delete new typing from ScrapTE}
  378.   Reset_EditMenu(UndoTyping);
  379.  END; {Redo_Typing}
  380.  
  381. PROCEDURE Undo;
  382.  
  383.  BEGIN
  384.   CASE EditStatus OF
  385.    UndoCut:
  386.     Undo_Cut;
  387.    UndoCopy:
  388.     Undo_Copy;
  389.    UndoPaste:
  390.     Undo_Paste;
  391.    UndoClear:
  392.     Undo_Clear;
  393.    UndoTyping:
  394.     Undo_Typing;
  395.    RedoCut:
  396.     Cut;
  397.    RedoCopy:
  398.     Copy;
  399.    RedoPaste:
  400.     Paste;
  401.    RedoClear:
  402.     Clear;
  403.    RedoTyping:
  404.     Redo_Typing;
  405.    OTHERWISE
  406.   END; {CASE EditStatus}
  407.  END; {Undo}
  408.  
  409.  
  410. PROCEDURE Edit_Manager (MenuItem:integer);
  411.  
  412.  BEGIN
  413.   CASE MenuItem OF
  414.    UndoItem:
  415.     Undo;
  416.    CutItem:
  417.     Cut;
  418.    CopyItem:
  419.     Copy;
  420.    PasteItem:
  421.     Paste;
  422.    ClearItem:
  423.     Clear;
  424.   END; {CASE MenuItem}
  425.  END; {Edit_Manager}
  426.  
  427. PROCEDURE Menu_Selector(where:Point;VAR QuitFlag:boolean);
  428.  
  429.  VAR theCode  : LongInt;
  430.      MenuNum  : integer;
  431.      MenuItem : integer;
  432.      
  433.  BEGIN
  434.   theCode:=MenuSelect(where);
  435.   MenuNum:=HiWord(theCode);
  436.   MenuItem:=LoWord(theCode);
  437.   Case MenuNum OF
  438.    FileMenu:File_Manager(MenuItem,QuitFlag);
  439.    EditMenu:Edit_Manager(MenuItem);
  440.    OTHERWISE
  441.   END; {CASE OF MenuNum}
  442.   HiliteMenu(0);
  443.  END; {Menu_Selector}
  444.  
  445. PROCEDURE TE_Selector(where:Point;extend:Boolean);
  446.  
  447.  BEGIN
  448.   SetPort(theWindow);                {Ensure currentport is the text window}
  449.   GlobaltoLocal(where);              {Put mouse point local to text window }
  450.   TEClick(where,extend,DisplayTE);
  451.   Reset_EditMenu(CantUndo);
  452.  END; {TE_Selector}
  453.  
  454. PROCEDURE Typist(EventMessage:LongInt);
  455.  
  456.  CONST Return    = $0D;
  457.        Enter     = $03;
  458.        Backspace = $08;
  459.        Tab       = $09;
  460.   
  461.   TYPE Codes = 0..255;
  462.  
  463.   VAR KeyCode      : integer;
  464.       CharIn       : Char;
  465.       CharH        : CharsHandle;
  466.       AllowedCodes : SET OF Codes;
  467.  
  468.  BEGIN
  469.   KeyCode:=BitAnd(EventMessage,charCodeMask);
  470.   AllowedCodes:=[$20..$7F,Return,Enter,Backspace,Tab];
  471.   {May be erroneous if used directly in TML Pascal 1.11 IF statement}
  472.   {See letter from Christopher Dunn in July 1986 MacTutor}
  473.   IF KeyCode in AllowedCodes THEN
  474.    CharIn:=chr(KeyCode)
  475.   ELSE
  476.    KeyCode:=0;  {Use KeyCode=0 as test to bypass sections}
  477.   IF EditStatus<>UndoTyping THEN
  478.    IF KeyCode<>0 THEN
  479.     BEGIN
  480.      Save_EndPoints;
  481.      Delete_ScrapTE;
  482.      Save_Selection(UndoStart,UndoEnd);
  483.     END; {IF EditStatus<>UndoTyping,IF KeyCode<>0}
  484.   IF KeyCode=Backspace THEN
  485.    WITH DisplayTE^^ DO
  486.     IF SelStart>0 THEN
  487.      IF SelEnd<=CurrentStart THEN
  488.       BEGIN
  489.        CharH:=TEGetText(DisplayTE);
  490.        {Get the text as a character array}
  491.        CurrentStart:=CurrentStart-1;
  492.        TESetSelect(0,0,ScrapTE);
  493.        TEKey(CharH^^[SelStart-1],ScrapTE);
  494.       END;
  495.   IF KeyCode<>0 THEN
  496.    BEGIN
  497.     TEKey(CharIn,DisplayTE);
  498.     If EditStatus<>UndoTyping THEN
  499.      Reset_EditMenu(UndoTyping);
  500.    END;
  501.  END; {Typist}
  502.  
  503. PROCEDURE MainEventLoop;
  504.  
  505.  VAR MainEvent : EventRecord;
  506.      theCode   : integer;
  507.       extend    : Boolean;
  508.       anyWindow : WindowPtr;
  509.  
  510.  BEGIN {MainEventLoop}
  511.   REPEAT
  512.    IF GetNextEvent(everyEvent,MainEvent) THEN
  513.     CASE MainEvent.what OF
  514.      activateEvt:
  515.       IF theWindow<>NIL THEN
  516.        TEActivate(DisplayTE);
  517.      mouseDown:
  518.       BEGIN
  519.        theCode:=FindWindow(MainEvent.where,anyWindow);
  520.        CASE theCode OF
  521.         inMenuBar:
  522.          Menu_Selector(MainEvent.where,QuitFlag);
  523.         inContent:         {Assume only one window}
  524.          BEGIN
  525.           extend:=(BitAnd(MainEvent.modifiers,ShiftKey)<>0);
  526.                   {If user holding shift key, then extend selection}
  527.           TE_Selector(MainEvent.where,extend);
  528.          END;
  529.        OTHERWISE           {Ignore}
  530.        END; {CASE theCode}
  531.       END; {mouseDown}
  532.       keyDown,autoKey:  {ignores command key}
  533.       IF theWindow<>NIL THEN
  534.        Typist(MainEvent.message);
  535.     OTHERWISE
  536.     END; {IF GetNextEvent, CASE MainEvent.what}
  537.    IF theWindow<>NIL THEN            {If window exists, blink caret}
  538.     TEIdle(DisplayTE);
  539.   UNTIL QuitFlag;
  540.  END; {MainEventLoop}
  541.  
  542. FUNCTION Init_MyMenus:Boolean;
  543.  
  544.  CONST MenuBarId = 200;
  545.  
  546.  VAR theMenuBar : Handle; {MBAR resource points to menus}
  547.  
  548.  BEGIN
  549.   Init_MyMenus:=FALSE;     {Assume menus not initialized}
  550.   theMenuBar:=GetNewMBar(MenuBarId);
  551.   IF theMenuBar<>NIL THEN
  552.    BEGIN
  553.     SetMenuBar(theMenuBar);
  554.     DrawMenuBar;
  555.     FileHandle:=GetMHandle(FileMenu);
  556.     EditHandle:=GetMHandle(EditMenu);
  557.     Init_MyMenus:=TRUE;
  558.    END;
  559.  END; {Init_MyMenus}
  560.  
  561. BEGIN {Main Program}
  562.  InitGraf(@thePort);
  563.  InitFonts;
  564.  InitWindows;
  565.  InitMenus;
  566.  TEInit;
  567.  InitDialogs(NIL);
  568.  InitCursor;
  569.  Init_MyGlobals;
  570.  FlushEvents(EveryEvent,0);
  571.  IF Init_MyMenus THEN
  572.   MainEventLoop;
  573. END. {Main Program}